package bento.api;

import java.util.*;

import bento.api.IBentoClient.IManager;
import bento.api.IBentoClient.IStoreManager.IStore;
import bento.api.IQuerier.IAppendThen;
import bento.api.IQuerier.IField;
import bento.api.IQuerier.ILogicMode;
import bento.api.IQuerier.IWhereThen;

public abstract class AbstractBentoClient implements IBentoClient {
    
    private static abstract class _AbstractManager_Base<TElem extends IManager.IElem>
                            implements IList<TElem>, 
                                       ICreatable<TElem>, 
                                       IRefreshAble, 
                                       ICommitAble,
                                       IWhereable<
                                           IQuerier<TElem>
                                       > {
        
        private List<TElem> localDB= new ArrayList<TElem>();
        private Queue<Func0<Boolean>> commits= new LinkedList<>();
        //private Stack<Func0<Boolean>> undos= new Stack<>();
        
        private ITransactor<TElem> transactor;
        private IQuerier<TElem> querier;
        
        private Func0<Boolean> makeAddCommit(final TElem elem) {
            return new Func0<Boolean>() {
                @Override
                public Boolean call() {
                    return transactor.add(elem);
                }
            };
        }
        
        private Func0<Boolean> makeRemoveCommit(final TElem elem) {
            return new Func0<Boolean>() {
                @Override
                public Boolean call() {
                    return transactor.remove(elem);
                }
            };
        }
        
        private Func0<Boolean> makeUpdateCommit(final TElem elem) {
            return new Func0<Boolean>() {
                @Override
                public Boolean call() {
                    return transactor.update(elem);
                }
            };
        }
        
//        protected void addUpdataCommit(TElem elem) {
//            commits.add(makeUpdateCommit(elem));
//        }
        
        //protected abstract TElem wrap(TWrapped elem);
        
        protected final void setTransactor(ITransactor<TElem> transactor_virtualDb) {
            this.transactor= transactor_virtualDb;
        }
        
        protected final void setQuerier(IQuerier<TElem> querier_virtualDb) {
            this.querier= querier_virtualDb;
        }

        @Override
        public void alert(TElem elem) {
            commits.add(makeUpdateCommit(elem));
        }

        @Override
        public <T> IWhereThen<IQuerier<TElem>, T> where(IField<T> field) {
            return querier.where(field);
        }

        @Override
        public void whereReset() {
            querier.reset();
        }

        @Override
        public void commit() {
            Func0<Boolean> c;
            while ((c= commits.poll())!=null) {
                if (c.call()==null) {
                    throw new RuntimeException("commit fail");
                }
            }
        }
    
        @Override
        public void refresh() {
            commits.clear();
            localDB.clear();
            for (TElem elem : querier.query()) {
                TElem w = elem;
                w.refresh();
                localDB.add(w);
            }
        }
    
        @Override
        public int size() {
            return localDB.size();
        }
    
        @Override
        public boolean isEmpty() {
            return localDB.isEmpty();
        }
    
        @Override
        public boolean contains(Object o) {
            return localDB.contains(o);
        }
    
        @Override
        public Iterator<TElem> iterator() {
            final Iterator<TElem> itor = localDB.iterator();
            return new Iterator<TElem>() {
                private TElem current;
                
                @Override
                public boolean hasNext() {
                    return itor.hasNext();
                }
    
                @Override
                public TElem next() {
                    current= itor.next();
                    return current;
                }
    
                @Override
                public void remove() {
                     _AbstractManager_Base<TElem> _this = _AbstractManager_Base.this;
                     _this.remove(current);
                }
            };
        }
    
        @Override
        public Object[] toArray() {
            return localDB.toArray();
        }
    
        @Override
        public <T> T[] toArray(T[] a) {
            return localDB.toArray(a);
        }
    
        @Override
        public boolean add(TElem e) {
            if (!commits.offer(makeAddCommit(e))) return false;
            if (!localDB.add(e)) {
                commits.poll();
                return false;
            }
            return true;
        }
    
        @SuppressWarnings("unchecked")
        @Override
        public boolean remove(Object o) {
            if (!commits.offer(makeRemoveCommit((TElem)o))) return false;
            if (!localDB.remove(o)) {
                commits.poll();
                return false;
            }
            
            return true;
        }
    
        @Override
        public boolean containsAll(Collection<?> c) {
            return c.containsAll(c);
        }
    
        @Override
        public boolean addAll(Collection<? extends TElem> c) {
            for (TElem e : c) {
                if (add(e)!=true) return false;
            }
            return true;
        }
    
        @Override
        public boolean addAll(int index, Collection<? extends TElem> c) {
            throw new RuntimeException("no implement");
        }
    
        @Override
        public boolean removeAll(Collection<?> c) {
            for (Object e : c) {
                if (remove(e)!=true) return false;
            }
            return true;
        }
    
        @Override
        public boolean retainAll(Collection<?> c) {
            throw new RuntimeException("no implement");
        }
    
        @Override
        public void clear() {
            for (Object e : localDB.toArray()) {
                if (remove(e)!=true) {
                    throw(new RuntimeException("clear fail"));
                }
            }
        }
    
        @Override
        public TElem get(int index) {
            return localDB.get(index);
        }
    
        @Override
        public TElem set(int index, TElem element) {
            throw(new RuntimeException("no implement"));
        }
    
        @Override
        public void add(int index, TElem element) {
            if (!commits.offer(makeAddCommit(element))) throw new RuntimeException("add fail");
            try {
                localDB.add(index, element);
                commits.poll();
            } catch (Exception e) {
                throw new RuntimeException("add fail");
            } 
        }
    
        @Override
        public TElem remove(int index) {
            if (!commits.offer(makeRemoveCommit(localDB.get(index)))) return null;
            
            TElem r;
            if ((r=localDB.remove(index)) == null) {
                commits.poll();
            }
            
            return r;
        }
    
        @Override
        public int indexOf(Object o) {
            return localDB.indexOf(o);
        }
    
        @Override
        public int lastIndexOf(Object o) {
            return localDB.lastIndexOf(o);
        }
    
        @Override
        public ListIterator<TElem> listIterator() {
            throw(new RuntimeException("no implement"));
        }
    
        @Override
        public ListIterator<TElem> listIterator(int index) {
            throw(new RuntimeException("no implement"));
        }
    
        @Override
        public List<TElem> subList(int fromIndex, int toIndex) {
            return localDB.subList(fromIndex, toIndex);
        }
        
        @Override
        public String toString() {
            String s= "[";
            
            for (TElem elem : this) {
                s+= elem.toString() +", ";
            }
            s+= "]";
            s= s.replace(", ]", "]");
            
            return s;
        }
        
        private static interface Func0<TR> {
            public TR call();
        }
    }
    
    public static abstract class AbstractManager<TElem extends IManager.IElem> 
                           extends _AbstractManager_Base<TElem>
                           implements IManager<TElem> {
        
       public interface IWrapper<T> {
           public T get();
       }
    }
}
